import * as React from 'react';
import ReactPlayer from 'react-player';

const Axios = require('axios');

import Search from '../components/search';
import Detail from '../components/detail';
import PlayerController from '../components/player';
import Progress from '../components/progress';
import Footer from '../components/footer';

import { Track, PlayMode } from './types';

const BaiduMusicApiUrl = 'http://tingapi.ting.baidu.com/v1/restserver/ting';
const BaiduMusicQuery = {
  /**
   * 'type=' Group type id
   * 'size=' return max count
   * 'offset=' start offset
   */
  group: 'method=baidu.ting.billboard.billList',
  /**
   * 'query=' Song title
   */
  search: 'method=baidu.ting.search.catalogSug',
  /**
   * 'songid=' Song Id
   */
  play: 'method=baidu.ting.song.play'
};

const BaiduGroupLists = {
  New: {
    title: '新歌',
    type: '1'
  },
  Hot: {
    title: '热歌',
    type: '2'
  },
  Rock: {
    title: '摇滚',
    type: '11'
  },
  Jess: {
    title: '爵士',
    type: '12'
  },
  Popular: {
    title: '流行',
    type: '16'
  },
  Western: {
    title: '欧美',
    type: '21'
  },
  OldFashion: {
    title: '经典老歌',
    type: '22'
  },
  Love: {
    title: '情歌对唱',
    type: '23'
  },
  Movie: {
    title: '影视金曲',
    type: '24'
  },
  Web: {
    title: '网络歌曲',
    type: '25'
  }
};

export class AppContainer extends React.Component {
  player: ReactPlayer;
  // playlist
  tracks: Array<Track>;

  // cached ordered playlist
  trackStack: Array<Track>;

  // current playing track
  track: Track;
  // seek position
  /**
   * Play mode:
   *     'random' random
   *     'single' loop single
   *     'all' loop all
   */
  mode: PlayMode;

  state: {
    mode: PlayMode;
    // for Player
    url: string;
    muted: boolean;
    playing: boolean;
    seeking: boolean;
    volume: number;
    played: number;
    loaded: number;
    duration: number;
    playbackRate: number;
    
    // for Search
    autoCompleteValue: string;
  };

  constructor(props: {}) {
    super(props);
    this.track = {title: '', streamUrl: '', artworkUrl: ''};
    this.tracks = new Array<Track>();
    this.trackStack = new Array<Track>(20);

    // Initial State
    this.state = {
      mode: PlayMode.RANDOM,
      url: '',
      muted: false,
      playing: true,
      seeking: false,
      volume: 0.8,
      played: 0,
      loaded: 0,
      duration: 0,
      playbackRate: 1.0,
      autoCompleteValue: ''
    };
  }

  load = (url: string) => {
    this.setState({
      url,
      played: 0,
      loaded: 0,
      playing: true
    });
  }
  playPause = () => {
    this.setState({ playing: !this.state.playing });
  }
  stop = () => {
    this.setState({ url: null, playing: false });
  }
  setVolume = (e: React.MouseEvent<HTMLInputElement>) => {
    let target = e.target as HTMLInputElement;
    this.setState({ volume: parseFloat(target.value) });
  }
  toggleMuted = () => {
    this.setState({ muted: !this.state.muted });
  }
  setPlaybackRate = (e: React.MouseEvent<HTMLInputElement>) => {
    let target = e.target as HTMLInputElement;
    this.setState({ playbackRate: parseFloat(target.value) });
  }
  
  forward = () => {
    // console.log(`forward`);
    
    this.stop();

    switch (this.state.mode) {
      case PlayMode.RANDOM:
        this.randomTrack();
        this.trackStack.push(this.track);
        break;

      case PlayMode.ALL:
        let index = this.tracks.indexOf(this.track);
        index = (index + 1) % this.tracks.length;
        this.track = this.tracks[index];
        this.load(this.track.streamUrl);
        this.trackStack.push(this.track);
        break;

      case PlayMode.SINGLE:
        this.load(this.track.streamUrl);
        break;
      
      default:
        this.randomTrack();
        this.trackStack.push(this.track);
        break;
    }
  }

  backward = () => {
    // console.log(`backward`);

    this.stop();

    switch (this.state.mode) {
      case PlayMode.RANDOM:
      case PlayMode.ALL:
        let track = this.trackStack.pop();
        if (track !== undefined) {
          this.track = track;
          this.load(this.track.streamUrl);
        }
        break;

      case PlayMode.SINGLE:
        this.load(this.track.streamUrl);
        break;
      default:
        this.load(this.track.streamUrl);
        break;
    }
  }

  changePlayMode = () => {
    const MODELIST = [PlayMode.RANDOM, PlayMode.ALL, PlayMode.SINGLE];
    let index = MODELIST.indexOf(this.state.mode);
    index = (index + 1) % MODELIST.length;
    let mode = MODELIST[index];

    // console.log(`changePlayMode ${PlayMode[this.state.mode]} -> ${PlayMode[mode]}`);
    this.setState({mode});
  }

  // Player callback
  onBuffer = () => {
    // console.log(`onBuffer`);
  }
  onReady = () => {
    // console.log(`onReady`);
  }
  onSeek = () => {
    // console.log(`onSeek`);
  }
  onPlay = () => {
    this.setState({ playing: true });
  }
  onPause = () => {
    this.setState({ playing: false });
  }
  onSeekMouseDown = (e: React.MouseEvent<HTMLProgressElement>) => {
    this.setState({ seeking: true });
  }
  onSeekChange = (e: React.MouseEvent<HTMLProgressElement>) => {
    let target = e.target as HTMLInputElement;
    this.setState({ played: parseFloat(target.value) });
  }
  onSeekMouseUp = (e: React.MouseEvent<HTMLProgressElement>) => {
    let target = e.target as HTMLInputElement;
    this.setState({ seeking: false });
    this.player.seekTo(parseFloat(target.value));
  }
  onProgress = (state: { played: number, loaded: number }) => {
    // We only want to update time slider if we are not currently seeking
    if (!this.state.seeking) {
      this.setState(state);
    }
  }
  onDuration = (duration: number) => {
    this.setState({ duration });
  }
  onStart = () => {
    // console.log(`onEnded`);
  }
  onEnded = () => {
    // console.log(`onEnded`);
    this.forward();
  }
  onError = (e: {}) => {
    // console.log(`onError`, e);
    if (this.tracks.length > 0) {
      this.forward();
    }
  }
  
  ref = (player: ReactPlayer) => {
    this.player = player;
  }

  // componentDidMount lifecycle method. Called once a component is loaded
  componentDidMount() {
    // console.log('componentDidMount');
    this.queryBaiduMusicList(BaiduGroupLists.Hot.type);
  }

  render() {
    const musicStyle = {
      width: '500px',
      height: '500px',
      backgroundImage: `linear-gradient(
      rgba(0, 0, 0, 0.7),
      rgba(0, 0, 0, 0.7)
      ),   url(${this.track.artworkUrl})`
    };
    return (
      <div className="music_layer" style={musicStyle}>
        <Search
          autoCompleteValue={this.state.autoCompleteValue}
          tracks={this.tracks}
          handleSelect={this.handleSelect}
          handleChange={this.handleChange}
        />
        <Detail title={this.track.title} />
        <ReactPlayer
          width="100%"
          height="0"
          ref={this.ref}
          url={this.state.url}
          playing={this.state.playing}
          volume={this.state.volume}
          muted={this.state.muted}
          onReady={this.onReady}
          onStart={this.onStart}
          onEnded={this.onEnded}
          onPlay={this.onPlay}
          onPause={this.onPause}
          onBuffer={this.onBuffer}
          onSeek={this.onSeek}
          onProgress={this.onProgress}
          onDuration={this.onDuration}
          onError={this.onError}
        />
        <PlayerController
          playing={this.state.playing}
          togglePlay={this.playPause}
          stop={this.stop}
          forward={this.forward}
          backward={this.backward}
          random={this.changePlayMode}
        />
        <Progress
          elapsed={this.formatSeconds(this.state.played * this.state.duration)}
          total={this.formatSeconds(this.state.duration)}
          played={this.state.played}
          loaded={this.state.loaded}
          duration={this.state.duration}
          onSeekMouseDown={this.onSeekMouseDown}
          onSeekMouseUp={this.onSeekMouseUp}
          onSeekChange={this.onSeekChange}
        />
        <Footer />
      </div>
    );
  }

  queryBaiduMusicList(type: string) {
    interface Song {
      song_id: string;
    }
    interface Response {
      data: {
        song_list: Array<Song>;
      };
    }

    let _self = this;

    // console.log(`queryBaiduMusicList(${type})`);

    // clear up data
    this.tracks.slice();
    this.trackStack.slice();

    Axios.get(`${BaiduMusicApiUrl}?${BaiduMusicQuery.group}&type=${type}`)
      .then(function(response: Response) {

        // console.log(`queryBaiduMusicList res=${JSON.stringify(response.data)}`);

        let data = response.data;
        let length = data.song_list.length;
        for (let i = 0; i < length; i++) {
          
          let song: Song = data.song_list[i];

          _self.queryBaiduMusicDetail(song.song_id);
        }
      })
      .catch(function(err: {}) {
        // console.log(err);
      });
  }

  searchBaiduMusicSong(title: string) {
    interface Song {
      songid: string;
    }
    interface Response {
      data: {
        song: Array<Song>;
      };
    }
    let _self = this;

    // console.log(`searchBaiduMusicSong(${title})`);

    // clear up data
    this.tracks.slice();
    this.trackStack.slice();

    Axios.get(`${BaiduMusicApiUrl}?${BaiduMusicQuery.search}&query=${title}`)
      .then(function(response: Response) {

        // console.log(`searchBaiduMusicSong res=${JSON.stringify(response.data)}`);
        
        let data = response.data;
        let length = data.song.length;
        for (let i = 0; i < length; i++) {
          _self.queryBaiduMusicDetail(data.song[i].songid);
        }
      })
      .catch(function(err: {}) {
        // console.log(err);
      });
  }

  queryBaiduMusicDetail(songId: string) {
    interface Response {
      data: {
        songinfo: {
          title: string,
          pic_premium: string,
        },
        bitrate: {
          file_link: string,
        },
      };
    }
    let _self = this;

    // console.log(`queryBaiduMusicDetail(${songId})`);

    Axios.get(`${BaiduMusicApiUrl}?${BaiduMusicQuery.play}&songid=${songId}`)
      .then(function(response: Response) {

        // console.log(`queryBaiduMusicDetail res=${JSON.stringify(response.data)}`);

        let data = response.data;
        let track = new Track(
          data.songinfo.title,
          data.bitrate.file_link,
          data.songinfo.pic_premium
        );

        // console.log(`queryBaiduMusicDetail track: ${track}`);
        _self.tracks.push(track);
      })
      .catch(function(err: {}) {
        // console.log(err);
      });
  }

  handleSelect = (value: string, item: Track) => {
    this.setState({ autoCompleteValue: value, url: item.streamUrl });
  }

  handleChange = (event: MouseEvent, value: string) => {
    let target = event.target as HTMLInputElement;
    // Update input box
    this.setState({ autoCompleteValue: target.value });
    // Search for song with entered value
    this.searchBaiduMusicSong(this.state.autoCompleteValue);
  }

  formatSeconds = (seconds: number) => {
    // Format hours
    // let hours = Math.floor(seconds / 3600);
    seconds = seconds % 3600;

    // Format minutes
    let minutes = Math.floor(seconds / 60);
    seconds = Math.floor(seconds % 60);

    // Return as string
    return (
      (minutes < 10 ? '0' : '') +
      minutes +
      ':' +
      (seconds < 10 ? '0' : '') +
      seconds
    );
  }

  randomTrack = () => {
    // Store the length of the tracks
    const trackLength = this.tracks.length;

    // console.log(`randomTrack len=${trackLength}`);

    // Pick a random number
    const randomNumber = Math.floor(Math.random() * trackLength + 1);
    this.track = this.tracks[randomNumber];
    
    this.load(this.track.streamUrl);
  }
}
